package org.kl.bf.service;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.kl.bf.dto.BasicStatistic;
import org.kl.bf.utils.CRUDUtils;
import org.kl.bf.utils.KLConvertUtils;
import org.kl.bf.utils.KLDynamicSpecifications;
import org.kl.bf.utils.MD5Util;

/**
 * @Author hqm
 * @CreateTime 2014年9月29日 上午9:19:34
 */
public class TreeStatisticUtils {
	/**
	 * 树形层级统计
	 * 
	 * @param resultClazz
	 * @param searchParams
	 * @param groupByColumns
	 * @param em
	 * @param criteriaBuilder
	 * @param resultMap
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static <T extends BasicStatistic> Map<String, T> statisitcTree(Class<T> resultClazz, CriteriaQuery<Object[]> criteriaQuery,
			Root root, Predicate[] predicates, Selection[] dataSelections, String[][] valueAndType, String groupByColumns,
			EntityManager em, CriteriaBuilder criteriaBuilder, Map<String, T> resultMap) {
		criteriaQuery.groupBy(new Expression[0]);
		resultMap = resultMap == null ? new TreeMap<String, T>() : resultMap;

		if (dataSelections == null || valueAndType[0].length != valueAndType[1].length || valueAndType[0].length != dataSelections.length) {
			throw new ServiceException("传入的数据列有问题，请检查是否为空，或者二位数组中长度不一致");
		}
		String[] groups = StringUtils.split(groupByColumns, ",");
		for (int i = -1; i < groups.length; i++) {// -1表示总计
			if (i != -1) {
				String[] thisGroups = new String[i + 1];
				System.arraycopy(groups, 0, thisGroups, 0, i + 1);
				Expression[] titleSelections = buildSelections(thisGroups, root);
				Selection[] selections = ArrayUtils.addAll(titleSelections, dataSelections);
				criteriaQuery.groupBy(titleSelections);

				if (predicates != null) {
					criteriaQuery.where(predicates);
				}
				criteriaQuery.orderBy(buildOrders(criteriaBuilder, thisGroups, root));
				criteriaQuery.select(criteriaBuilder.array(selections));
			} else {
				if (predicates != null) {
					criteriaQuery.where(predicates);
				}
				criteriaQuery.select(criteriaBuilder.array(dataSelections));
			}
			List<Object[]> result = em.createQuery(criteriaQuery).getResultList();
			for (Object[] arr : result) {
				T line = null;
				String titleKey = CRUDUtils.contractArray(arr, 0, i);
				try {
					line = resultMap.get(titleKey);
					if (line == null) {
						line = resultClazz.newInstance();
						resultMap.put(titleKey, line);
					}
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
				try {
					BeanUtils.setProperty(line, "state", i == -1 ? "open" : (i == groups.length - 1 ? null : "closed"));//
					BeanUtils.setProperty(line, "title", i == -1 ? "[总计]" : (StringUtils.isBlank((String) arr[i]) ? '空' : (String) arr[i]));//
					BeanUtils.setProperty(line, "ptitle",
							i == -1 ? "" : (i == 0 ? "[总计]" : MD5Util.mD5Encode(CRUDUtils.contractArray(arr, 0, i - 1))));
					BeanUtils.setProperty(line, "id", i == -1 ? "[总计]" : MD5Util.mD5Encode(CRUDUtils.contractArray(arr, 0, i)));
					BeanUtils.setProperty(line, "pid", i == -1 ? "[总计]" : "[总计]" + CRUDUtils.contractArray(arr, 0, i));
					BeanUtils.setProperty(line, "cs", i + 1);
					for (int j = 0; j < valueAndType[0].length; j++) {
						BeanUtils.setProperty(line, (String) valueAndType[0][j], KLConvertUtils.convertStringToObject(
								arr[i + 1 + j] == null ? "0" : arr[i + 1 + j].toString(), (String) valueAndType[1][j]));
					}
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}
		}
		return resultMap;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Expression[] buildSelections(String[] groups, Root root) {
		Expression[] selections = new Expression[groups.length];
		for (int i = 0; i < groups.length; i++) {
			selections[i] = KLDynamicSpecifications.buildPath(root, groups[i]);
		}
		return selections;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static List<Order> buildOrders(CriteriaBuilder criteriaBuilder, String[] groups, Root root) {
		List<Order> orderList = new ArrayList<Order>();
		for (int i = 0; i < groups.length; i++) {
			orderList.add(criteriaBuilder.asc(KLDynamicSpecifications.buildPath(root, groups[i])));
		}
		return orderList;
	}

	/**
	 * 导出成Excel时，按树层级关系，添加空格，使其在Excel也形成属性结构
	 * 
	 * @param resultList
	 */
	public static <T extends BasicStatistic> void changeExportTitle(List<T> resultList) {
		for (int i = 0; i < resultList.size(); i++) {
			int n = resultList.get(i).getCs();
			String title = resultList.get(i).getTitle();
			for (int j = 0; j < n; j++) {
				title = "    " + title;
			}
			resultList.get(i).setTitle(title);
		}
	}
}
